//----------------------------------------------------------------
//module name : yhz_axi_rw
//engineer : yhz
//date : 2021.10.02
//----------------------------------------------------------------
`include "yhz_defines.v"
module yhz_axi_rw (
    input  wire        clock           ,
    input  wire        reset           ,
    //user
	input  wire        rw_valid_i      ,
	output wire        rw_ready_o      ,
    input  wire        rw_req_i        ,
    output wire [63:0] data_read_o     ,
    input  wire [63:0] data_write_i    ,
    input  wire [63:0] data_mask_i     ,
    input  wire [63:0] rw_addr_i       ,
    input  wire [1:0]  rw_size_i       ,
    //Advanced_eXtensible_Interface
    //write_address
    input  wire        axi_aw_ready_i  ,
    output wire        axi_aw_valid_o  ,
    output wire [63:0] axi_aw_addr_o   ,
    output wire [2:0]  axi_aw_prot_o   ,
    output wire [3:0]  axi_aw_id_o     ,
    output wire        axi_aw_user_o   ,
    output wire [7:0]  axi_aw_len_o    ,
    output wire [2:0]  axi_aw_size_o   ,
    output wire [1:0]  axi_aw_burst_o  ,
    output wire        axi_aw_lock_o   ,
    output wire [3:0]  axi_aw_cache_o  ,
    output wire [3:0]  axi_aw_qos_o    ,
    output wire [3:0]  axi_aw_region_o ,
    //write_data
    input  wire        axi_w_ready_i   ,
    output wire        axi_w_valid_o   ,
    output wire [63:0] axi_w_data_o    ,
    output wire [7:0]  axi_w_strb_o    ,
    output wire        axi_w_last_o    ,
    output wire        axi_w_user_o    ,
    //write_resp
    output wire        axi_b_ready_o   ,
    input  wire        axi_b_valid_i   ,
    input  wire [1:0]  axi_b_resp_i    ,
    input  wire [3:0]  axi_b_id_i      ,
    input  wire        axi_b_user_i    ,
    //read_address
    input  wire        axi_ar_ready_i  ,
    output wire        axi_ar_valid_o  ,
    output wire [63:0] axi_ar_addr_o   ,
    output wire [2:0]  axi_ar_prot_o   ,
    output wire [3:0]  axi_ar_id_o     ,
    output wire        axi_ar_user_o   ,
    output wire [7:0]  axi_ar_len_o    ,
    output wire [2:0]  axi_ar_size_o   ,
    output wire [1:0]  axi_ar_burst_o  ,
    output wire        axi_ar_lock_o   ,
    output wire [3:0]  axi_ar_cache_o  ,
    output wire [3:0]  axi_ar_qos_o    ,
    output wire [3:0]  axi_ar_region_o ,
    //read_data
    output wire        axi_r_ready_o   ,
    input  wire        axi_r_valid_i   ,
    input  wire [1:0]  axi_r_resp_i    ,
    input  wire [63:0] axi_r_data_i    ,
    input  wire        axi_r_last_i    ,
    input  wire [3:0]  axi_r_id_i      ,
    input  wire        axi_r_user_i     
);
//----------------------------------------------------------------
//wire
//----------------------------------------------------------------
    //command
    wire w_trans = rw_req_i   == 1'b1    ;
    wire r_trans = rw_req_i   == 1'b0    ;
    wire w_valid = rw_valid_i &  w_trans ;
    wire r_valid = rw_valid_i &  r_trans ;
    // handshake
    wire aw_hs = axi_aw_ready_i & axi_aw_valid_o ;
    wire w_hs  = axi_w_ready_i  & axi_w_valid_o  ;
    wire b_hs  = axi_b_ready_o  & axi_b_valid_i  ;
    wire ar_hs = axi_ar_ready_i & axi_ar_valid_o ;
    wire r_hs  = axi_r_ready_o  & axi_r_valid_i  ;
    //done
    wire w_done     = w_hs & axi_w_last_o     ;
    wire r_done     = r_hs & axi_r_last_i     ;
    wire trans_done = w_trans ? b_hs : r_done ;
//----------------------------------------------------------------
//State_Machine
//----------------------------------------------------------------
    parameter [2:0] W_STATE_IDLE = 3'b000 , W_STATE_ADDR = 3'b001 , W_STATE_WRITE = 3'b010 , W_STATE_RESP = 3'b011 , W_STATE_WAIT = 3'b100 ;
    parameter [2:0] R_STATE_IDLE = 3'b000 , R_STATE_ADDR = 3'b001 , R_STATE_READ  = 3'b010 , R_STATE_WAIT = 3'b011 ;

    reg [2:0] w_state, r_state ;
    wire w_state_idle = w_state == W_STATE_IDLE , w_state_addr = w_state == W_STATE_ADDR , w_state_write = w_state == W_STATE_WRITE , w_state_resp = w_state == W_STATE_RESP ;
    wire r_state_idle = r_state == R_STATE_IDLE , r_state_addr = r_state == R_STATE_ADDR , r_state_read  = r_state == R_STATE_READ ;
    //Wirte_State_Machine
    always @(posedge clock) begin
        if (reset) begin
            w_state <= W_STATE_IDLE ;
        end
        else begin
            if (w_valid) begin
                case (w_state)
                    W_STATE_IDLE  :             w_state <= W_STATE_ADDR  ;
                    W_STATE_ADDR  : if (aw_hs)  w_state <= W_STATE_WRITE ; else w_state <= W_STATE_ADDR  ;
                    W_STATE_WRITE : if (w_done) w_state <= W_STATE_RESP  ; else w_state <= W_STATE_WRITE ;
                    W_STATE_RESP  : if (b_hs)   w_state <= W_STATE_WAIT  ; else w_state <= W_STATE_RESP  ;
                    W_STATE_WAIT  :             w_state <= W_STATE_IDLE  ;
                    default       :             w_state <= W_STATE_IDLE  ;
                endcase
            end
            else begin
                w_state <= W_STATE_IDLE ;
            end
        end
    end
    //Read_State_Machine
    always @(posedge clock) begin
        if (reset) begin
            r_state <= R_STATE_IDLE ;
        end
        else begin
            if (r_valid) begin
                case (r_state)
                    R_STATE_IDLE :             r_state <= R_STATE_ADDR ;
                    R_STATE_ADDR : if (ar_hs)  r_state <= R_STATE_READ ; else r_state <= R_STATE_ADDR ;
                    R_STATE_READ : if (r_done) r_state <= R_STATE_WAIT ; else r_state <= R_STATE_READ ;
                    R_STATE_WAIT :             r_state <= R_STATE_IDLE ;
                    default      :             r_state <= R_STATE_IDLE ;
                endcase
            end
            else begin
                r_state <= R_STATE_IDLE ;
            end
        end
    end
//----------------------------------------------------------------
//Process_Data
//----------------------------------------------------------------
    //rw_ready
    reg rw_ready ;
    wire rw_ready_nxt = trans_done ;
    wire rw_ready_en  = trans_done | rw_ready ;
    always @(posedge clock) begin
        if (reset) begin
            rw_ready <= 0 ;
        end
        else if (rw_ready_en) begin
            rw_ready <= rw_ready_nxt ;
        end
    end
    assign rw_ready_o = rw_ready ;
    //strb
    wire mask_0 = data_mask_i[7 :0 ] == 8'hff ;
    wire mask_1 = data_mask_i[15:8 ] == 8'hff ;
    wire mask_2 = data_mask_i[23:16] == 8'hff ;
    wire mask_3 = data_mask_i[31:24] == 8'hff ;
    wire mask_4 = data_mask_i[39:32] == 8'hff ;
    wire mask_5 = data_mask_i[47:40] == 8'hff ;
    wire mask_6 = data_mask_i[55:48] == 8'hff ;
    wire mask_7 = data_mask_i[63:56] == 8'hff ;
    wire [7:0] strb = {mask_7 , mask_6 , mask_5 , mask_4 , mask_3 , mask_2 , mask_1 , mask_0} ;
//----------------------------------------------------------------
//Write_Transaction
//----------------------------------------------------------------
    //write_address
    assign axi_aw_valid_o  = w_state_addr  ;
    assign axi_aw_addr_o   = rw_addr_i     ;
    assign axi_aw_prot_o   = 0             ;
    assign axi_aw_id_o     = 0             ;
    assign axi_aw_user_o   = 0             ;
    assign axi_aw_len_o    = 0             ;
    assign axi_aw_size_o   = 3'b011        ;
    assign axi_aw_burst_o  = 2'b01         ;
    assign axi_aw_lock_o   = 0             ;
    assign axi_aw_cache_o  = 4'b0010       ;
    assign axi_aw_qos_o    = 0             ;
    assign axi_aw_region_o = 0             ;
    //write_data
    assign axi_w_valid_o   = w_state_write ;
    assign axi_w_data_o    = data_write_i  ;
    assign axi_w_strb_o    = strb          ;
    assign axi_w_last_o    = w_state_write ;
    assign axi_w_user_o    = 0             ;
    //write_resp
    assign axi_b_ready_o   = w_state_resp  ;
//----------------------------------------------------------------
//Read_Transaction
//----------------------------------------------------------------
    //read_address
    assign axi_ar_valid_o = r_state_addr ;
    assign axi_ar_addr_o  = rw_addr_i    ;
    assign axi_ar_prot_o  = 0            ;
    assign axi_ar_id_o    = 0            ;
    assign axi_ar_user_o  = 0            ;
    assign axi_ar_len_o   = 8'd0         ;
    assign axi_ar_size_o  = 3'b011       ;
    assign axi_ar_burst_o = 2'b01        ;
    assign axi_ar_lock_o  = 1'b0         ;
    assign axi_ar_cache_o = 4'b0010      ;
    assign axi_ar_qos_o   = 4'h0         ;
    //read_data
    assign axi_r_ready_o  = r_state_read ;
//----------------------------------------------------------------
//to_cpu
//----------------------------------------------------------------
    //data_read_o
    reg [63:0] data_read ;
    always @(posedge clock) begin
        if(reset) begin
            data_read <= 64'd0 ;
        end
        else begin
            data_read <= axi_r_data_i ;
        end
    end
    assign data_read_o = data_read ;
//----------------------------------------------------------------
endmodule
//----------------------------------------------------------------
